﻿using Microsoft.Extensions.Logging;
using S7.Net;
using System.Threading.Channels;
using static Api.Shared.Utils.MyCopyProperty;

namespace Api.Shared.MyPlc.S7
{
    public class S7NioService<T> : IS7ConnService
        where T : class, new()
    {
        private int errorTimes = 0;

        //private PeriodicTimer timer = new(TimeSpan.FromMilliseconds(100));
        private CancellationTokenSource cts = new();
        public bool MyIsConnected { get; set; }

        public Plc? S7Plc { get; set; }

        private Channel<T> messageChannel;
        private readonly ILogger<S7NioService<T>> logger;
        private T nioData;

        public S7NioService(ILogger<S7NioService<T>> logger, T nioData)
        {
            this.logger = logger;
            this.nioData = nioData;

            var opt = new BoundedChannelOptions(2000)
            {
                FullMode = BoundedChannelFullMode.DropOldest,
                Capacity = 2000
            };
            messageChannel = Channel.CreateBounded<T>(opt);
            Task.Run(async () =>
            {
                await foreach (T item in messageChannel.Reader.ReadAllAsync())
                {
                    CopyProperties(item, this.nioData);
                    //this.nioData = item;
                }
            });
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="myIp">Ip地址</param>
        /// <param name="dbNum">Db号</param>
        /// <param name="startByte">起始字节</param>
        public void ConnPlc(string myIp, int dbNum, int startByte)
        {

            Task.Factory.StartNew(
                async () =>
                {
                    //using PeriodicTimer timer = new(TimeSpan.FromMilliseconds(100));

                    using (PeriodicTimer timer = new(TimeSpan.FromMilliseconds(300)))
                    {
                        while (!cts.IsCancellationRequested && await timer.WaitForNextTickAsync())
                        {
                            if (S7Plc == null || !MyIsConnected)
                            {
                                try
                                {
                                    S7Plc = new Plc(CpuType.S71500, myIp, 0, 1);
                                    S7Plc.Open();
                                    S7Plc.ReadTimeout = 620;
                                    S7Plc.WriteTimeout = 620;
                                    MyIsConnected = S7Plc.IsConnected;
                                    if (MyIsConnected)
                                    {
                                        logger.LogInformation("PlcSucessConn!");
                                    }
                                }
                                catch (Exception ex)
                                {
                                    if (errorTimes > 200)
                                    {
                                        errorTimes = 0;
                                        logger.LogError("重试连接次数超过200");
                                    }
                                    errorTimes++;
                                    S7Plc?.Close();
                                    S7Plc = null;
                                    MyIsConnected = false;
                                    logger.LogError($"{ex.Message}:{DateTime.Now}");
                                    logger.LogError("重连次数:" + errorTimes);
                                    await Task.Delay(2000);
                                }
                            }
                            else if (MyIsConnected)
                            {
                                try
                                {
                                    MyIsConnected = S7Plc.IsConnected;
                                    errorTimes = 0;
                                    var dbDataTemp = await S7Plc.ReadClassAsync<T>(
                                        dbNum,
                                        startByte
                                    );
                                    //T nioDataTemp = new();
                                    //CopyProperties(dbDataTemp, nioDataTemp);
                                    if (dbDataTemp != null)
                                    {
                                        await messageChannel.Writer.WriteAsync(dbDataTemp);
                                    }
                                    //messageChannel.Writer.Complete(); //告诉reader 写完成信号
                                }
                                catch (Exception ex)
                                {
                                    if (errorTimes > 200)
                                    {
                                        errorTimes = 0;
                                        logger.LogError("读取时发生错误次数超过200");
                                    }
                                    errorTimes++;
                                    await Task.Delay(2000);

                                    logger.LogError($"读取时发生错误:{ex.Message}:{DateTime.Now}");
                                    logger.LogError($"读取时发生错误次数:{errorTimes}");
                                    S7Plc.Close();
                                    MyIsConnected = false;
                                    S7Plc = null;
                                }
                            }
                        }
                    }
                    ;
                },
                cts.Token,
                TaskCreationOptions.LongRunning,
                TaskScheduler.Default
            );
        }

        public void ConnPlc(string myIp)
        {
            return;
        }
    }
}
